#region License
// Copyright 2008-2009 Jeremy Skinner (http://www.jeremyskinner.co.uk)
// 
// Licensed under the Apache License, Version 2.0 (the "License"); 
// you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at 
// 
// http://www.apache.org/licenses/LICENSE-2.0 
// 
// Unless required by applicable law or agreed to in writing, software 
// distributed under the License is distributed on an "AS IS" BASIS, 
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
// See the License for the specific language governing permissions and 
// limitations under the License.
// 
// The latest version of this file can be found at http://www.codeplex.com/FluentValidation
#endregion

namespace FluentValidation.Tests {
	using System;
	using System.Linq;
	using Internal;
	//using Moq;
	using NUnit.Framework;
	using Results;
	using Validators;

	[TestFixture]
	public class RuleBuilderTests {
		RuleBuilder<Person, string> builder;

		[SetUp]
		public void Setup() {
			builder = new RuleBuilder<Person, string>("Surname", x => x.Surname);
		}

		[Test]
        // ReSharper disable InconsistentNaming
		public void Should_build_property_name() {

			builder.Model.Identifier.ShouldEqual("Surname");
		}

		[Test]
		public void Should_compile_expression() {
			var person = new Person {Surname = "Foo"};
			builder.Model.PropertyFunc(person).ShouldEqual("Foo");
		}

		[Test]
		public void Adding_a_validator_should_return_builder() {
			var builderWithOptions = builder.SetValidator(new TestPropertyValidator());
			builderWithOptions.ShouldBeTheSameAs(builder);
		}

		[Test]
		public void Adding_a_validator_should_store_validator() {
			var validator = new TestPropertyValidator();
			builder.SetValidator(validator);
			builder.Cast<ISimplePropertyRule<Person>>().Single().Validator.ShouldBeTheSameAs(validator);
		}

		[Test]
		public void Should_set_cutom_property_name() {
			builder.SetValidator(new TestPropertyValidator()).WithName("Foo");
			Assert.That(builder.Model.CustomPropertyName, Is.EqualTo("Foo"));
		}

		[Test]
		public void Should_set_custom_error() {
			builder.SetValidator(new TestPropertyValidator()).WithMessage("Bar");
			builder.Cast<PropertyRule<Person, string>>().Single().CustomValidationMessage.ShouldEqual("Bar");
		}

		[Test]
		public void Should_throw_if_validator_is_null() {
			typeof(ArgumentNullException).ShouldBeThrownBy(() => builder.SetValidator((IPropertyValidator<Person, string>)null));
		}

		[Test]
		public void Should_throw_if_overriding_validator_is_null() {
			typeof(ArgumentNullException).ShouldBeThrownBy(() => builder.SetValidator((IValidator<string>)null));
		}

		[Test]
		public void Should_throw_if_message_is_null() {
			typeof(ArgumentNullException).ShouldBeThrownBy(() => builder.SetValidator(new TestPropertyValidator()).WithMessage(null));
		}

		[Test]
		public void Should_throw_if_property_name_is_null() {
			typeof(ArgumentNullException).ShouldBeThrownBy(() => builder.SetValidator(new TestPropertyValidator()).WithName(null));
		}

		[Test]
		public void Should_throw_when_predicate_is_null() {
			typeof(ArgumentNullException).ShouldBeThrownBy(() => builder.SetValidator(new TestPropertyValidator()).When(null));
		}

		[Test]
		public void Should_throw_when_inverse_predicate_is_null() {
			typeof(ArgumentNullException).ShouldBeThrownBy(() => builder.SetValidator(new TestPropertyValidator()).Unless(null));
		}

		[Test]
		public void Calling_when_should_replace_current_validator_with_predicate_validator() {
			var validator = new TestPropertyValidator();
			builder.SetValidator(validator).When(x => true);
			builder.Cast<ISimplePropertyRule<Person>>().Single().Validator.ShouldBe<DelegatingValidator<Person, string>>();

			var predicateValidator = (DelegatingValidator<Person, string>)builder.Cast<ISimplePropertyRule<Person>>().Single().Validator;
			predicateValidator.InnerValidator.ShouldBeTheSameAs(validator);
		}

		[Test]
		public void PropertyDescription_should_return_property_name_split() {
			var ruleBuilder = new RuleBuilder<Person, DateTime>("DateOfBirth", x => x.DateOfBirth);
			ruleBuilder.Model.PropertyDescription.ShouldEqual("Date Of Birth");
		}

		[Test]
		public void PropertyDescription_should_return_custom_property_name() {
            var ruleBuilder = new RuleBuilder<Person, DateTime>("DateOfBirth", x => x.DateOfBirth);
            ruleBuilder.NotEqual(default(DateTime)).WithName("Foo");
            ruleBuilder.Model.PropertyDescription.ShouldEqual("Foo");
		}

		[Test]
		public void Nullable_object_with_condition_should_not_throw() {
            var ruleBuilder = new RuleBuilder<Person, int>("NullableIntValue", x => x.NullableInt.Value);
            ruleBuilder.GreaterThanOrEqualTo(3).When(x => x.NullableInt != null);
            ruleBuilder.Single().Validate(new ValidationContext<Person>(new Person(), new PropertyChain(), new DefaultValidatorSelector()));
		}

		[Test]
		public void Rule_for_a_non_memberexpression_should_not_generate_property_name() {
            var ruleBuilder = new RuleBuilder<Person, int>("", x => x.CalculateSalary());
            ruleBuilder.Model.PropertyDescription.ShouldEqual("");
            ruleBuilder.Model.Identifier.ShouldEqual("");
		}

		[Test]
		public void Should_throw_when_no_validator_specified() {
			typeof(InvalidOperationException).ShouldBeThrownBy(() => builder.Single().Validate(new ValidationContext<Person>(new Person(), new PropertyChain(), new DefaultValidatorSelector())));
		}

		[Test]
		public void Property_should_return_property_being_validated() {
			var property = typeof(Person).GetProperty("Surname");
            builder.Model.Identifier.ShouldEqual(property.Name);
		}

		class TestPropertyValidator : IPropertyValidator<Person, string> {
			public PropertyValidatorResult Validate(PropertyValidatorContext<Person, string> context) {
				return null;
			}
		}
        // ReSharper restore InconsistentNaming
	}
}